package com.dkd.manage.task;

import com.dkd.common.constant.DkdContants;
import com.dkd.manage.domain.*;
import com.dkd.manage.domain.dto.TaskDetailsDto;
import com.dkd.manage.domain.dto.TaskDto;
import com.dkd.manage.domain.vo.ChannelVo;
import com.dkd.manage.service.*;
import com.dkd.manage.task.allocator.AdvancedEmployeeAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class AutoTask {

    @Autowired
    private IVendingMachineService vendingMachineService;

    @Autowired
    private IChannelService channelService;

    @Autowired
    private ITaskService taskService;

    @Autowired
    private INodeService nodeService;

    @Autowired
    private IEmpService empService;

    @Autowired
    private AdvancedEmployeeAllocator employeeAllocator;

    // 该集合用于存储达到阈值的售货机编码
    private final Set<String> innerCodes = new HashSet<>();

    /**
     * 自动创建工单
     */
    // @Scheduled(cron = "*/5 * * * * ?")
    // @Scheduled(cron = "0 0 8-20 * * ?") // 早上8点到晚上8点,每小时执行一次
    public void autoCreateTask() {
        log.info("开始执行自动创建工单任务");
        // 1.查询所有的售货机查看容器是否低于阈值
        VendingMachine vendingMachine = new VendingMachine();
        List<VendingMachine> vendingMachineList = vendingMachineService.selectVendingMachineList(vendingMachine);
        if (Optional.ofNullable(vendingMachineList).isEmpty()) {
            log.error("售货机集合为空");
            return;
        }

        // 过滤掉状态不是运营的售货机
        vendingMachineList = vendingMachineList.stream().filter(v -> v.getVmStatus().equals(DkdContants.VM_STATUS_OPERATION)).collect(Collectors.toList());

        // 2.遍历售货机集合
        for (VendingMachine machine : vendingMachineList) {
            // 获取设备当前总容量
            Long currentTotalCapacity = calculateCurrentTotalCapacity(machine.getInnerCode());
            // 获取设备最大总容量
            Long channelMaxCapacity = machine.getChannelMaxCapacity();
            // 计算阈值   阈值的字段存的是2位数的整数 例如 存的值为20 则为20%
            long thresholdValue = (long) (machine.getThreshold() * 0.01 * channelMaxCapacity);

            // 判断设备容量是否小于阈值
            if (currentTotalCapacity < thresholdValue) {
                // 如果能进这个if判断,说明售货机需要补货,需要创建补货工单
                try {
                    // 构建taskDto对象
                    TaskDto taskDto = buildTaskDto(machine.getInnerCode());
                    taskService.insertTaskDto(taskDto);
                    // 把当前售货机编码添加进集合
                    innerCodes.add(machine.getInnerCode());
                    // 修改当前售货机补货,autoCreateCount字段 + 1
                    machine.setAutoCreateCount(machine.getAutoCreateCount() + 1);
                    vendingMachineService.updateVendingMachine(machine);
                } catch (Exception e) {
                    log.error("自动创建工单异常:{}", e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
        // 3.检测autoCreateCount字段是否超过3
        checkAutoCreateCount();
    }

    /**
     * 用于构建taskDto对象
     *
     * @return taskDto对象
     */
    private TaskDto buildTaskDto(String innerCode) {
        TaskDto taskDto = new TaskDto();
        taskDto.setInnerCode(innerCode);
        taskDto.setUserId(processUserId(innerCode)); // 设置业务员id
        taskDto.setProductTypeId(DkdContants.TASK_TYPE_SUPPLY); // 2表示补货工单
        taskDto.setDesc("AI智能自动创建工单");
        taskDto.setCreateType(DkdContants.AUTO_CREATE_TASK); // 0表示自动创建
        // 处理 工单详情(只有补货工单才会涉及)
        List<ChannelVo> channelVos = channelService.selectChannelVoListByInnerCode(innerCode);
        List<TaskDetailsDto> detailsDtoList = channelVos.stream().map(channelVo -> {
            TaskDetailsDto detailsDto = new TaskDetailsDto();
            detailsDto.setChannelCode(channelVo.getChannelCode());
            detailsDto.setExpectCapacity(channelVo.getMaxCapacity());
            detailsDto.setSkuId(channelVo.getSkuId());
            detailsDto.setSkuName(Optional.ofNullable(channelVo.getSku()).map(Sku::getSkuName).orElse(null));
            detailsDto.setSkuImage(Optional.ofNullable(channelVo.getSku()).map(Sku::getSkuImage).orElse(null));
            return detailsDto;
        }).collect(Collectors.toList());
        taskDto.setDetails(detailsDtoList);
        return taskDto;
    }

    /**
     * 获取售货机所在区域的业务员id
     *
     * @param innerCode 售货机编号
     * @return 业务员id
     */
    private Long processUserId(String innerCode) {
        VendingMachine vendingMachine = vendingMachineService.selectVendingMachineByInnerCode(innerCode);
        Node node = nodeService.selectNodeById(vendingMachine.getNodeId());
        List<Emp> empList = empService.selectEmpByRegionId(node.getRegionId());

        if (empList.isEmpty()) {
            log.warn("区域 {} 没有可用的业务员", node.getRegionId());
            return null;
        }

        // 使用高级多算法融合系统选择员工
        Long selectedEmpId = employeeAllocator.selectOptimalEmployee(
                innerCode, empList, "SUPPLY"); // 补货任务

        log.info("高级算法选择结果 - 设备: {}, 员工: {}", innerCode, selectedEmpId);
        return selectedEmpId;
//        VendingMachine vendingMachine = vendingMachineService.selectVendingMachineByInnerCode(innerCode);
//        Node node = nodeService.selectNodeById(vendingMachine.getNodeId());
//        List<Emp> empList = empService.selectEmpByRegionId(node.getRegionId());
//        // 该集合得到的就是售货机所在区域的所有业务员 在其中随机挑选一个即可
//        // todo 这里需要考虑加上负载均衡的算法挑选员工,并不能直接使用随机获取
//        Emp emp = empList.get((int) (Math.random() * empList.size()));
//        return emp.getId();
    }

    /**
     * 计算售货机当前总容量
     *
     * @param innerCode 售货机编号
     * @return 当前总容量
     */
    private Long calculateCurrentTotalCapacity(String innerCode) {
        Channel queryChannel = new Channel();
        queryChannel.setInnerCode(innerCode);
        List<Channel> channels = channelService.selectChannelList(queryChannel);

        if (Optional.ofNullable(channels).isEmpty()) {
            return 0L;
        }

        // 因为可能有的售货机货道并没有商品,因此做一个健壮性的判断,如果商品为空,则当前货道的容量为0
        return channels.stream().mapToLong(ch -> Optional.ofNullable(ch.getCurrentCapacity()).orElse(0L)).sum();
    }

    /**
     * 该方法用于检测autoCreateCount字段是否超过3
     */
    public void checkAutoCreateCount() {
        innerCodes.forEach(innerCode -> {
            VendingMachine vendingMachine = vendingMachineService.selectVendingMachineByInnerCode(innerCode);
            if (vendingMachine.getAutoCreateCount() >= DkdContants.OVER_AUTO_CREATE_COUNT) {
                // 更新当前设备的阈值为50
                vendingMachine.setThreshold(DkdContants.OVER_THRESHOLD);
                vendingMachineService.updateVendingMachine(vendingMachine);
            }
        });
    }

    /**
     * 该方法用于重置autoCreateCount字段和阈值
     */
    // @Scheduled(cron = "*/5 * * * * ?")
    // @Scheduled(cron = "0 1 20 * * ?") // 每天晚上8点01执行一次
    public void resetAutoCreateCountAndThreshold() {
        log.info("开始重置自动创建工单字段和阈值");
        innerCodes.forEach(innerCode -> {
            log.info("开始重置自动创建工单字段和阈值 {}", innerCode);
            VendingMachine vendingMachine = vendingMachineService.selectVendingMachineByInnerCode(innerCode);
            vendingMachine.setAutoCreateCount(DkdContants.AUTO_CREATE_COUNT); // 重置autoCreateCount字段为0
            vendingMachine.setThreshold(DkdContants.THRESHOLD); // 重置阈值为20
            vendingMachineService.updateVendingMachine(vendingMachine);
        });

        // 清理innerCodes集合
        innerCodes.clear();

        /*
        备用方案 每天晚上8点01的时候去查询最新的数据
        List<VendingMachine> vendingMachines = vendingMachineService.selectVendingMachineList(new VendingMachine());
        vendingMachines.forEach(vendingMachine -> {
            if (vendingMachine.getThreshold() > 20 && vendingMachine.getAutoCreateCount() > 0) {
                // 说明需要重置
                vendingMachine.setAutoCreateCount(DkdContants.AUTO_CREATE_COUNT); // 重置autoCreateCount字段为0
                vendingMachine.setThreshold(DkdContants.THRESHOLD); // 重置阈值为20
                vendingMachineService.updateVendingMachine(vendingMachine);
            }
        });*/
    }
}
